// pages/learn/jsbase/js_grammar/Js_grammar.js

/**
 * var：声明一个变量，格式：var 变量名 = 变量值
 *    
 * let：声明一个变量，格式同var
 * 
 * const：声明一个常量(只赋值一次，后面不可改，如果是声明对象，对也不可改，但对象内的属性可改)，格式同var
 * 
 */
var a = 1 // 定义并赋值一个变量a，此时是全局变量
var a = 10 // 不会报错，即能声明同变量名的变量，这个也容易出现错乱，所以一般不适用var声明了
let b = 2 // 定义并赋值一个变量b，此时是全局变量
// let b = 20  // 报错：Identifier 'b' has already been declared
const c = 3
const car = {
  brand: '丰田',
  price: 100000
}

/**
 * 变量常量学习
 */
function var_let_const() {
  // Js_grammar.js:30 1 a = 2
  // Js_grammar.js:30 2 a = 3
  // Js_grammar.js:44 3 a = 1
  // var a = 2 // 当方法内与外部通变量同名时，a就近原则，仅变化方法内的a。不影响外部的a
  // console.log('1 a = ' + a)
  // a = a + 1 
  // Js_grammar.js:33 2 a = 2
  // Js_grammar.js:47 3 a = 2 // 当仅在外边定义了var a,某方法内改变后全局也变了。
  // console.log('2 a = ' + a)
  var a = 10
  var a = 5
  console.log('4 a = ' + a)

  let b = 22
  // let b = 21 // Identifier 'b' has already been declared.
  // Js_grammar.js? [sm]:45 b = 22
  // Js_grammar.js? [sm]:59 3 a = 10, b = 2 。 全局变量与Java的一样。只是可以在方法内部再次声明。
  console.log('b = ' + b)
  // c = 30 // 常量修改值会报错： "c" is read-only
  console.log('c = ' + c)
  car.price = 200000 // 
  // car = {} //报错。 "car" is read-only
  console.log('car = ', car) //如果是声明对象，对也不可改，但对象内的属性可改
};

/**
 * 数据类型：
 * 基本类型：String、Number、Boolean、null、undefined
 * 对象;object---{}。
 */
function dataType() {
  // 声明一个字符串，单/双引号皆可。如果声明是字符串一般建议给默认值。以表示类型。因为如果不给默认值，是声明一个undefined变量
  // let username = 'Hui'; // 可加分号也可不加。一般的开发都会加
  const username = 'Hui'; // 可加分号也可不加。一般的开发都会加
  // 声明一个Number(包括整数、小数)数据
  let age = 30;
  let weight = 60.5; // 声明一个
  let rate = 0.5;
  // 声明一个boolean类型
  let isOpen = false;
  // 声明一个null
  let x = null; // null 与undefined的区别，null是一个值被定义了，定义为null。undefined是值为被定义
  // 声明一个undefined
  let y = undefined;
  let z;
  // Js_grammar.js? [sm]:69 age number, weight number, rate number, x object, y undefined, z undefined
  console.log('age ' + (typeof age) + ", weight " + (typeof weight) + ", rate " + (typeof rate) +
    ", x " + (typeof x) + ", y " + (typeof y) + ", z " + (typeof z));

  // 字符串常规使用，模板字符串，占位符字符串${}，这是es6推出的,结合反向单引号使用。
  console.log('My name is ' + username + ', and I am  ' + age + ' yeas old');
  console.log(`My name is ${username}, and I am  ${age} yeas old`); // 需要反向单引号，一般在键盘数字的1的左边
  const s = ' Hello World!  ';
  // Js_grammar.js:79 len(含2头的空格) 15, toUp  HELLO WORLD!  , sub(头的空格也占1位) Hell, split  (4) [" He", "", "o Wor", "d!  "]
  console.log('len(含2头的空格) ' + s.length + ', toUp ' + s.toUpperCase() + ', sub(头的空格也占1位)' + s.substring(0, 5) +
    ', split ', s.split('l')); // split分割字符串成数组，分割后改字符不见

  // 数组
  // 声明一个数字类型的数组
  // const numbers = new Array(1, 2, 3, 4, 5, 5.5) // 构造函数创建
  const arrs = [1, 2, 3, 4, 5, 5.5] // 等同于上行
  console.log(arrs) //  [1, 2, 3, 4, 5, 5.5]
  console.log('numbers' + arrs.toString()) // 1,2,3,4,5,5.5
  arrs.push(6) // push 在末尾添加元素
  arrs.unshift(false) // unshift 在头部添加元素
  console.log(arrs) //  [false, 1, 2, 3, 4, 5, 5.5,6]
  arrs.push({
    name: "Hui"
  })
  arrs.push(true)
  // 删除末尾元素
  arrs.pop();
  console.log(arrs) //  [false, 1, 2, 3, 4, 5, 5.5,6,{...}]。所以默认都是对象数组。
  const fruits = ['apples', 'oranges', 'pears', true, 1.5]
  // const fruits = ['apples', 'oranges', 'pears', true, 1.5] //  Identifier 'fruits' has already been declared 常量数组不能被重新声明，但能修改内部元素
  // 获取数组元素：数组名[索引]，索引从0开始
  // 改变元素
  fruits[1] = 'banana'
  console.log('1 索引的元素 ' + fruits[1])
  // 判断是否数组  fruits is array true, username is array false
  console.log('fruits is array ' + Array.isArray(fruits) + ', username is array ' + Array.isArray(username))
  // 获取元素所在索引
  fruits.push(true)
  console.log('true index ' + fruits.indexOf(true), fruits) // true index 3（即第四个） (6) ["apples", "banana", "pears", true, 1.5, true]

  // 对象 使用{}包裹的键值对(key:（英文的冒号）Value)。一个对象中属性可以是任何类型，即也可以null、对象。属性之间需要用英文的逗号分隔
  const person = {
    name: 'Hui',
    age: 18,
    hobbies: ['music', 'read', 'sports'],
    address: {
      street: '50 main st',
      city: "shenzhen",
      state: 'MA'
    }
  }
  //  person.toString() -- [object Object]
  // 获取、修改属性；对象.属性名
  person.age = 28
  console.log('str ' + person.toString() + ', 属性 age ' + person.age + ', address city ' + person.address.city, person)

  // 结构，相当于新建几个变量将值从Person中取出，必须与Person的属性名对应。不能跨级。
  // const {name, age1, city,hobbies} = person; // name Hui, age1 undefined, city undefined (3) ["music", "read", "sports"]
  const {
    name,
    age1,
    address: {
      city
    },
    hobbies
  } = person;
  console.log('name ' + name + ', age1 ' + age1 + ', city ' + city, hobbies);
  // 增加新的属性
  person.email = 'hui.163.com';
  console.log(person)

  // 对象数组
  const todos = [{
    id: 1,
    text: 'Take out trash',
    isComplete: true
  }, {
    id: 2,
    text: 'Meeting with Boss',
    isComplete: false
  }, {
    id: 3,
    text: 'Dentist appt',
    isComplete: false
  }, ]
  console.log('代办的第一内容:' + todos[1].text, todos);
  // json 是一种数据格式，以键值对的格式，主要用于网络数据传输。json与对象的差异在于json的key、value都是双引号包裹，对象的key不需要引号，value可以单/双引号。
  // 对象是不能直接用于网络数据传输的，不能与后台互传。
  const todoJson = JSON.stringify(todos); // 对象转json ,转完可用于网络传输
  console.log(todoJson)
  console.log('添加数组0', [...todos, ...arrs]) // 添加数据，重新定义数组
  todos.push(arrs); // 数组变成了最后一个元素
  console.log('添加数组', todos)
  let addArr = todos.concat(todos); // 数组添加数组。返回一个新的数组，原数组不变
  console.log('添加数组2', todos)
  console.log('添加数组3', addArr)
}

/**
 * 语法：syntax、grammar
 * if、for
 */
function syntax() {
  // 条件语句 if(条件) {条件成立} else {条件不成立}
  var isOpen = true;
  if (isOpen) {
    console.log('syntax isOpen ' + isOpen)
  }
  var n = 10;
  if (n == 20) {
    console.log('syntax is true')
  } else if (n > 10) {
    console.log('syntax is >10')
  } else {
    console.log('syntax is not >10 ', n)
  }
  // == 与 ===的区别, https://zhuanlan.zhihu.com/p/90621028
  // 简单来说： == 代表相同， ===代表严格相同（数据类型和值都相等）。
  //【理解原理】： 当进行双等号比较时候，先检查两个操作数数据类型，如果相同，则进行===比较，如果不同，则愿意为你进行一次类型转换，转换成相同类型后再进行比较，而===比较时，如果类型不同，直接就是false。
  // Mac:command + 回车，执行下一行。
  const n1 = 1; // true 在js底层值是1，false值是0。==会自动转换数值然后判断，===不转换类型判断，当类型不同时直接false。
  console.log('n1 1, n1 == true: ' + (n1 == true) + ', n1 === true: ' + (n1 === true))
  let n2 = 15;
  // 短路与&&、或||。与Java一样。
  if (n1 > 0 && n2 < 5) {
    console.log(` ${n1} > 0 && ${n2} < 5 true`)
  } else if (n1 > 0 && n2 < 10) {
    console.log(` ${n1} > 0 && ${n2} < 10 true`)
  } else {
    console.log(` other ${n1} ${n2}  true`)
  }
  // 三目运算符 let 变量 = 条件 ？ 值1(条件成立时) : 值2(条件不成立时)
  // n2 = 52;
  let color = n2 > 50 ? "green" : "red";
  console.log('color ' + color)

  let colorIndex;
  // color = 'other' // switch 走 default
  // 条件语句2 switch
  switch (color) { // 与Java基本相同,不同的是Js中switch可以是直接对象。
    case 'red':
      colorIndex = 1;
      break;
    case 'green':
      colorIndex = 2;
      break;
    case 'blue':
      colorIndex = 3;
      break;
    default:
      colorIndex = 0;
  }
  console.log('colorIndex = ' + colorIndex)
  let user1 = {
    id: 1,
    name: 'Hui',
    age: 18
  }
  const users = [{
    id: 1,
    name: 'zhi',
  }]
  users.push(user1)
  console.log('对象加入数组后取出是否不同了(相同) user1 == users[1]' + (user1 == users[1]) + ', === ' + (user1 === users[1]))
  switch (user1) { // 与Java基本相同,不同的是Js中switch可以是直接对象。
    case users[0]:
      console.log('user1 is users[0]');
      break
    case users[1]:
      console.log('user1 is users[1]'); // Js_grammar.js? [sm]:239 user1 is users[1]
      break
    default:
      console.log('user1 is other');
  }

  // for 循环，大致与Java一样
  for (let i = 0; i < 6; i++) {
    console.log(`for loop index  ${i}`)
  }
  // while 循环，大致与Java一样
  let accout; // 不初始化，是undefined。
  console.log('account < 5 = ' + (accout < 5) + ', type ' + typeof(accout))
  accout = 0;
  while (accout < 5) {
      accout++; // ++运算符与Java一样
      console.log('while ' + accout)
  }
  users.push({name:'lisi'})
  for (let i = 0; i < users.length; i++) {
    console.log(`for loop username  ${users[i].name}`)
  }
  // 另一种for
  for (let item of users) {
    console.log(`for --item-- username  ${item.name}`)
  }

}

Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    var_let_const() // 
    dataType();
    syntax();
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
    console.log('3 a = ' + a + ', b = ' + b) // 如果没在外部声明，仅在另一个方法里声明。会报错：a is not defined 
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})